package com.it.component;


import com.it.annonation.Autowired;
import com.it.annonation.Component;
import com.it.annonation.Qualifier;
import com.it.annonation.Value;
import com.it.utils.Tools;
import lombok.Data;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 2022年6月11日23:50:25
 */
@Data
//@Slf4j
public class AnnotationConfigApplicationContext {
    private static Map<String, Object> ioc = new ConcurrentHashMap<>();

    public AnnotationConfigApplicationContext(String packageNmae) {
        Set<BeanDefinition> beanDefinition = getBeanDefinition(packageNmae);
        initClass(beanDefinition);
        Autoload(beanDefinition);
    }

    /**
     * 自动装载
     *
     * @param beanDefinition
     */
    private void Autoload(Set<BeanDefinition> beanDefinition) {
        for (BeanDefinition definition : beanDefinition) {
            Class beanClass = definition.getBeanClass(); // 当前遍历的class
            String beanName = definition.getBeanName(); // 当前的beanname
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Autowired annotationAutowired = declaredField.getAnnotation(Autowired.class);
                Qualifier annotationQualifier = declaredField.getAnnotation(Qualifier.class);
                if (annotationAutowired != null) {  // 说明当前字段有这个注解
                    if (annotationQualifier != null) {
                        // 根据qualifier 里面的value从ioc容器中获取到对应的bean
                        String qualifierValue = annotationQualifier.value();
                        Object bean = getBean(qualifierValue);
//                        String simpleName = beanClass.getSimpleName();
//                        String str = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);

                        Object o = ioc.get(qualifierValue); // 获取到user对象 给里面的user赋值
                        setValue(beanClass, ioc.get(beanName), declaredField, Autowired.class, o);  // 然后赋值到对应的属性里
                    } else { // TODO type 类型注入    按照名字注入
                        String simpleName = declaredField.getType().getSimpleName(); // 得到类型的简单名字 User
                        String str = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1); // User -> user
                        Object o = ioc.get(str); // 根据order获取到对象，然后赋值
                        setValue(beanClass, ioc.get(beanName), declaredField, Autowired.class, o);
                    }
                }
            }
        }

    }


    /**
     * 初始化创建所有的目标类，并且赋值value
     *
     * @param beanDefinitionSet
     */
    private void initClass(Set<BeanDefinition> beanDefinitionSet) {
        for (BeanDefinition beanDefinition : beanDefinitionSet) {
            Class clazz = beanDefinition.getBeanClass();
            Component annotationComponent = (Component) clazz.getAnnotation(Component.class);
            if (annotationComponent != null) {
                String valueComponent = annotationComponent.value();
                if (!"".equals(valueComponent) && null != valueComponent) { // 如果component有值，就以这个为key
                    Object instance = getInstance(clazz);
                    Field[] declaredFields = clazz.getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        if (declaredField.getAnnotation(Value.class) != null) {  // 有这个注解就进行赋值
                            setValue(clazz, instance, declaredField, Value.class, null);
                        }
                    }
                    ioc.put(valueComponent, instance);  // 放入ioc缓存中
                } else {// 默认就以 Order -》 order为名
                    Object instance = getInstance(clazz);
                    String fullQualifiedName = clazz.getName(); // 全限定类名
                    String simpleName = clazz.getSimpleName();
                    String str = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1); // User -> user
                    Field[] declaredFields = clazz.getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        if (declaredField.getAnnotation(Value.class) != null) {  // 有这个注解就进行赋值
                            setValue(clazz, instance, declaredField, Value.class, null);
                        }
                    }
                    ioc.put(str, instance);  // 放入ioc缓存中
                }

            }
        }
    }


    private Set<BeanDefinition> getBeanDefinition(String packageNmae) {
        Set<Class<?>> classSet = Tools.getClasses(packageNmae);
        Iterator<Class<?>> iterator = classSet.iterator();
        Set<BeanDefinition> set = new HashSet<>();
        while (iterator.hasNext()) {
            Class<?> clazz = iterator.next();
            Component annotationComponent = clazz.getAnnotation(Component.class);
            if (annotationComponent != null) {
                String simpleName = clazz.getSimpleName();
                String str = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
                BeanDefinition beanDefinition = BeanDefinition.builder().beanName(str).beanClass(clazz).build();
                set.add(beanDefinition);
            }
        }
        return set;
    }

    /**
     * 通过set方法进行属性的赋值
     *
     * @param clazz         当前遍历的字节码， 比如是order
     * @param instance      当前的order实例
     * @param declaredField 当前order里对应的属性字段  一种引用类型。
     * @param obj           引用类型的对象
     */
    private void setValue(Class<?> clazz, Object instance, Field declaredField, Class<?> annoation, Object obj) {
        String fieldName = declaredField.getName(); // 字段名
        String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);// setNickname
        if (annoation.equals(Value.class)) {
            Value annotationValue = declaredField.getAnnotation(Value.class);
            String value = annotationValue.value();
            Method method = getMethod(clazz, methodName, declaredField.getType());// 方法名，字段的参数类型
            String fieldTypeName = declaredField.getType().getName();
            Object argValue = judgeTypeValue(fieldTypeName, value); // set方法值
            try {
                method.invoke(instance, argValue);   //  方法对象，方法参数  调用set方法，进行赋值
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } else if (annoation.equals(Autowired.class)) {  // 引用类型的注入
            System.out.println("methodName = " + methodName);
            System.out.println("fieldName = " + fieldName);
            Method method = getMethod(clazz, methodName, declaredField.getType());
            Object o = ioc.get(fieldName);
            try {
                method.invoke(instance, ioc.get(fieldName));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 根据名字从ioc容器中获取到一个对象
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        return ioc.get(beanName);
    }

    /**
     * \
     * 通过字节码获取到对应
     *
     * @param clazz
     * @return
     */
    private Object getInstance(Class<?> clazz) {
        try {
            return clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 得到一个方法
     *
     * @param clazz
     * @param methodName
     * @param parameterTypes
     * @return
     */
    private Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
        try {
            return clazz.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 判断注解value的值类型
     *
     * @param fieldTypeName
     * @return
     */
    private Object judgeTypeValue(String fieldTypeName, String value) {
        Object arg = null;
        if ("java.lang.String".equals(fieldTypeName)) {
            arg = value;
        } else if ("java.lang.Integer".equals(fieldTypeName)) {
            arg = Integer.parseInt(value);
        } else if ("java.lang.Double".equals(fieldTypeName)) {
            arg = Double.parseDouble(value);
        } else if ("java.lang.Float".equals(fieldTypeName)) {
            arg = Float.parseFloat(value);
        } else if ("java.lang.Short".equals(fieldTypeName)) {
            arg = Short.parseShort(value);
        } else if ("int".equals(fieldTypeName)) {
            arg = Integer.parseInt(value);
            //                TODO ....
        }

        return arg;
    }
}
